/*
MayPack v0.1
Copyright (c) 2021 Majjcom
licence:	MIT
Date:		2021/12/16
FILE:		packger.cpp
*/
#include <filesystem>
#include <cstdlib>
#include <cstdio>
#include <string>
#include <vector>
#include "include/mayjson/may.h"
#include "include/maypack.h"

#pragma warning(disable : 4996)
using namespace maypack;

struct Package::FileInfo
{
	std::string name;
	int pos;
	int size;
	bool have;
};

void Package::_prase_info(FILE* fp_in)
{
	char length_c[16];
	fread(length_c, sizeof(char), 16, fp_in);
	const int info_length = atoi(length_c);
	std::string json;
	for (int i = 0; i < info_length; i++)
	{
		json.append(1, fgetc(fp_in));
	}
	this->_info_s = json.c_str();
	this->_start_pos = int(ftell(fp_in));
	this->_info.setUp(this->_info_s);
	this->_prase_list();
}

void Package::_prase_list()
{
	int count = this->_info.getValue<int>("count");
	for (int i = 0; i < count; i++)
	{
		char index[4];
		itoa(i, index, 10);
		mayjson::Json* info_one = new mayjson::Json(this->_info.getValue<mayjson::Json>(index));
		this->_fileList.resize(this->_fileList.size() + 1);
		this->_fileList[i] = info_one;
	}
}

void Package::_cleanUp()
{
	int count = this->_fileList.size();
	for (int i = 0; i < count; i++)
	{
		this->_fileList[i]->release();
		delete this->_fileList[i];
	}
}

Package::FileInfo Package::_get_file_info(const char* name)
{
	const int count = this->_fileList.size();
	std::string name_get;
	int pos;
	int size;
	FileInfo ret;
	ret.have = false;

	for (int i = 0; i < count; i++)
	{
		name_get = this->_fileList[i]->getValue<const char*>("name");
		pos = this->_fileList[i]->getValue<int>("pos");
		size = this->_fileList[i]->getValue<int>("size");
		if (name_get == name)
		{
			ret.name = name_get;
			ret.pos = pos;
			ret.size = size;
			ret.have = true;
			break;
		}
	}
	return ret;
}
void Package::setUp(const char* fin)
{
	if (!this->_is_setuped)
	{
		
		if (std::experimental::filesystem::exists(std::experimental::filesystem::path(fin)))
		{
			this->_name = fin;
			FILE* fp_in = fopen(fin, "rb");
			this->_prase_info(fp_in);
			fclose(fp_in);
			this->_is_setuped = true;
		}
	}
}

Package::Package(const char* fin)
{
	this->setUp(fin);
}

maypack::Package::Package() {}

Package::~Package()
{
	this->close();
}

std::vector<std::string> Package::getFileList()
{
	if (!this->_is_setuped || this->_is_closed)
	{
		return std::vector<std::string>(0);
	}
	std::vector<std::string> ret;
	int count = this->_fileList.size();
	ret.resize(count);
	for (int i = 0; i < count; i++)
	{
		const char* name = this->_fileList[i]->getValue<const char*>("name");
		ret[i] = name;
	}
	return ret;
}

int Package::getFileSize(const char* name)
{
	if (!this->_is_setuped || this->_is_closed)
	{
		return 1;
	}
	FileInfo get = this->_get_file_info(name);
	return get.size;
}

int Package::unPack(const char* name)
{
	if (!this->_is_setuped || this->_is_closed)
	{
		return 2;
	}
	FileInfo info = this->_get_file_info(name);
	if (!info.have)
	{
		return 1;
	}
	FILE* fp_in = fopen(this->_name, "rb");
	fseek(fp_in, this->_start_pos + info.pos, SEEK_SET);
	FILE* fp_out = fopen(info.name.c_str(), "wb");
	char buff;
	for (int i = 0; i < info.size; i++)
	{
		fread(&buff, sizeof(char), 1, fp_in);
		fwrite(&buff, sizeof(char), 1, fp_out);
	}
	fclose(fp_in);
	fclose(fp_out);

	return 0;
}

int Package::unPackAll()
{
	std::vector<std::string> list = this->getFileList();
	int count = list.size();
	if (count == 0)
	{
		return 2;
	}
	int rc;
	for (int i = 0; i < count; i++)
	{
		rc = this->unPack(list[i].c_str());
		if (rc != 0)
		{
			return 1;
		}
	}
	return 0;
}

void Package::close()
{
	if (!this->_is_closed)
	{
		this->_cleanUp();
		this->_is_closed = true;
	}
}
